#!/bin/bash

# Update script for moblock.
#
# The general contract of this script is that it will attempt to download all
# blocklists.  If a blocklist fails to download, or download correctly (i.e.,
# partial dl, etc.), and an older version of the list exists, it will not be
# overwritten.  If one or more blocklists fail to download, but a previous
# version of that list is present in the cache, the script will re-compile the
# master list anyway and return zero.  However, if any of the list specified
# in BLOCKLISTS are not present (or usable), the script will *not* replace the
# existing master list, if there is one, and will not attempt to create one if
# there wasn't one already.
#
# This assures that under no condition, the update results in a less secure
# blocklist (aside from items being removed from one of the updated blocklists
# themselves).  At the same time, if any of the lists can be updated at all
# then it will be done.  These rules are especially important since some of
# the lists seem to be unavailable quite a lot of the time.

source /etc/conf.d/moblock
source /etc/init.d/functions.sh

log_file=/var/log/moblock-update.log
tmp_dir=/tmp/moblock-update.$$ 

typeset -i successful_dl_count=0
typeset -i failed_dl_count=0
typeset -i FAIL=0

cleanup() {
	rm -rf ${tmp_dir}
	log_msg "$0 exiting.

-------------------------------------------------------------------------------


"
}

die() {
	eerror "Update failed: $@" | tee -a ${log_file}
	eerror "See /var/log/moblock-update.log for details."
	cleanup
	exit 1;
}

log_msg() {
	echo -e "$(date): $@" >> ${log_file}
}

init() {
	if [ -z "${BLOCKLISTSERVERS}" ]; then
		eerror "There is no BLOCKLISTSERVERS defined in /etc/conf.d/moblock."
		eindent
		eerror "Please set this variable to the list of servers you wish to"
		eerror "download from."
		eoutdent
		
		FAIL=1
	fi

	if [ -z "${BLOCKLISTS}" ]; then
		eerror "There is no BLOCKLISTS defined in /etc/conf.d/moblock."
		eindent
		eerror "Please set this variable to the lists you wish to use."
		eoutdent
		
		FAIL=1
	fi

	if [ -z "${BLOCKLISTFILE}" ]; then
		eerror "There is no BLOCKLISTFILE defined in /etc/conf.d/moblock."
		eindent
		eerror "Please set this variable to the file you wish to output the"
		eerror "merged block list to."
		eoutdent
		
		FAIL=1
	fi

	if [ -z "${BLOCKLISTDIR}" ]; then
		eerror "There is no BLOCKLISTDIR defined in /etc/conf.d/moblock."
		eindent
		eerror "Please set this variable to the directory you wish to store"
		eerror "the downloaded lists in."
		eoutdent
		
		FAIL=1
	fi

	[ ${FAIL} -eq 0 ] || die "invalid configuration"
}

# Iterate through servers until we get one to work or they all fail. This
# function backs up old files before attempting to download so that if
# downloading fails, then we wont loose our old copy. The general contract is:
# * Only downloading the file when it's been updated (i.e., wget -N).
# * Never clobbering an old file until I'm sure that I downloaded the new one
#   correctly.
getAFile() {
	local tmp_log=${tmp_dir}/${1}.wget.log
	local fn=${1}.${BLOCKLISTSUFFIX}
	for base_url in ${BLOCKLISTSERVERS}; do
		test -f ${BLOCKLISTDIR}/${fn} && cp -p ${BLOCKLISTDIR}/${fn} ${tmp_dir}/${fn}.bak
		log_msg "Attempting to downloading ${fn} from location ${base_url}"
		if wget -P ${BLOCKLISTDIR} \
				-N ${base_url}/${fn} \
				--connect-timeout=45 \
				-a ${tmp_log}; then
			rm ${tmp_log}
			rm -f ${tmp_dir}/${fn}.bak
			return 0
		else
			test -f ${tmp_dir}/${fn}.bak && mv ${tmp_dir}/${fn}.bak ${BLOCKLISTDIR}/${fn}
		fi
	done

	log_msg "Failed to download ${fn}.
${BAD}wget output ---------->${NORMAL}
$(cat ${tmp_log})
${BAD}<---------- end of wget output${NORMAL}"
	rm ${tmp_log}
	return 1
}

getFiles() {
	einfo Downloading lists...
	eindent

	for i in "$@"; do
		ebegin "Downloading ${i}" | tee -a ${log_file}

		if getAFile $i; then
			successful_dl_count=${successful_dl_count}+1
			eend 0 | tee -a ${log_file}
		else
			failed_dl_count=${failed_dl_count}+1
			eend 1 | tee -a ${log_file}
		fi
	done

	eoutdent

	if [ ${failed_dl_count} -ne 0 ]; then
		if [ ${successful_dl_count} -eq 0 ]; then
			die "All downloads failed"
		else
			ewarn "WARNING: ${failed_dl_count} downloads failed!  See /var/log/moblock-update.log" \
				| tee -a ${log_file}
			ewarn "for details.  Previous blocklists will be used failed items." \
				| tee -a ${log_file}
		fi
	fi
}

mergeFiles() {
	einfo Unpacking and merging lists...
	eindent

	local new_p2p_file=${tmp_dir}/new.p2p

	for i in ${BLOCKLISTS}; do
		ebegin Merging ${i} | tee -a ${log_file}

		gunzip -c ${BLOCKLISTDIR}/${i}.${BLOCKLISTSUFFIX} >> ${new_p2p_file} 2>>${log_file} \
			|| die "Failed to extract list '${i}'"

		eend $? | tee -a ${log_file}
	done

	mv ${new_p2p_file} ${BLOCKLISTFILE}

	eoutdent
}

reloadList() {
	moblock_pid=$(cat /var/run/moblock.pid 2>/dev/null)

	if ps -p ${moblock_pid} > /dev/null 2>&1; then
		einfo "Reloading block list"
		kill -s HUP ${moblock_pid}
		eend $? 
	fi
}

main() {
	mkdir -p ${BLOCKLISTDIR} || die "Failed to create dir ${BLOCKLISTDIR}."
	mkdir -p ${tmp_dir} || die "Failed to create dir ${tmp_dir}"

	einfo "Updating moblock..." | tee -a ${log_file}
	eindent
	log_msg "$0 initiated."

	getFiles ${BLOCKLISTS} ${WHITELISTS}
	mergeFiles
	reloadList | tee -a ${log_file}

	eoutdent
	if [ ${failed_dl_count} -eq 0 ]; then
		einfo "MoBlock update completed successfully." | tee -a ${log_file}
	else
		ewarn "MoBlock update partially successful." | tee -a ${log_file}
	fi
	cleanup
}

main

